# coding:utf8
"""
思路：DB-->Sqlite(:memory:)
"""
import hashlib
import operator
import pickle
import time

from app.const.lang.tips_info import TipsInfo
from framework.dal import profile_model as Profiles
from framework.dal.dal_adapters import profile_adapters as pdts
from framework.dal.dal_adapters import profile_register_setting
from framework.firefly_dc.error_handle import GameException
from utils.print_utils import echo

OP = {
    'eq': operator.eq,
    'lt': operator.lt,
    'lte': operator.le,
    'gt': operator.gt,
    'gte': operator.ge,
    'ne': operator.ne,
    'in': operator.contains,
}


# 需要载入的配置表名
profile_table_name_set = set([item[0] for item in profile_register_setting.values()])


class MemProfileManager(object):
    """内存配置管理"""

    #: 内存映射key=table_name, value=adapter instance
    cache = {}

    @classmethod
    def init(cls):
        echo("开始加载内存配置")

        st = time.time()

        # 初始化所有表
        for table_name in profile_table_name_set:
            tc_model = getattr(Profiles, table_name)()
            adapter = getattr(pdts, table_name + "Adapter", None)

            if not tc_model:
                echo("{}未在PeeweeModel中定义".format(table_name))
                continue
            if not adapter:
                echo("{}未在ProfilesAdapter中定义".format(table_name + "Adapter"))
                continue

            records = adapter.objects.all()
            cls.cache[table_name] = records

            # TODO: 随机睡眠，总时间不超过10秒,Tc表目前为166张
            # time.sleep(random.randint(1, 60) / 1000.0)

        echo("加载完毕！耗时%fs" % (time.time() - st))

    @classmethod
    def update_mem_profile(cls, table_name_list):
        """
        更新MemSet里的特定表配置
        :param table_name_list: 多个表名
        :return:
        """
        for table_name in table_name_list:
            # 获取model
            try:
                tc_model = getattr(Profiles, table_name)()

            # 获取不到表
            except AttributeError:
                return False

            adapter = getattr(pdts, table_name + "Adapter", None)
            if not adapter:
                return False

            records = adapter.objects.all()
            cls.cache[table_name] = records

        return True

    @classmethod
    def pre_validate_profile_config(cls, table_name_list):
        """
        校验内存与数据库内表内容是否一致
        :param table_name_list:
        :return:
        """
        old_cache, new_cache = {}, {}
        for table_name in table_name_list:

            adapter = getattr(pdts, table_name + "Adapter", None)
            if not adapter:
                raise GameException("{}不存在".format(table_name))

            old_cache[table_name] = cls.cache[table_name]
            records = adapter.objects.all()
            new_cache[table_name] = records

        if cls.get_md5_cache(old_cache) == cls.get_md5_cache(new_cache):
            raise GameException("配置无变化")

        return True

    @classmethod
    def get_md5_cache(cls, cache_data):
        """
        计算缓存数据md5值
        :param cache_data:
        :return:
        """
        m = hashlib.md5()
        m.update(pickle.dumps(cache_data))
        return m.hexdigest()

    @classmethod
    def md5_profile(cls):
        """
        校验profile
        """
        m = hashlib.md5()
        m.update(pickle.dumps(cls.cache))
        return m.hexdigest()

    @classmethod
    def filter(cls, table_name, data=None, **kwargs):
        """
        过滤

        ..versionchanged: 增加SQL隐式转换

        :param str table_name: 表名
        :param dict kwargs: 参数 eg. Key=Value, Key__in=[], Key__glt=int
        """
        # 解析过滤条件 字段，操作，值 (field, pw_op, val)
        cdts = []
        tc_model = getattr(Profiles, table_name)

        for field_opt, val in kwargs.items():
            # 由__组成的表达式 Key__glt=int
            if '__' in field_opt:
                field, opt = field_opt.split('__')
                pw_op = OP.get(opt)

            # 赋值表达式 Key=Value
            else:
                field = field_opt
                pw_op = OP.get('eq')

            # 获取字段类型 比如'int'
            f_type = getattr(tc_model, field).db_field

            cdts.append((field, pw_op, val, f_type))

        def condition(obj):
            """筛选条件"""
            for field, pw_op, val, f_type in cdts:
                key = getattr(obj, field)
                # in 操作
                if pw_op == operator.contains:
                    # in 要反转参数 key=list val=
                    key, val = val, key

                    # SQL类型隐式转换: 查询条件str/unicode，db类型int
                    if all(map(lambda x: type(x) in (str, unicode), key)) and (f_type == 'int'):
                        key = map(int, key)

                    # SQL类型隐式转换: 查询条件int，db类型string
                    if all(map(lambda x: type(x) == int, key)) and (f_type == 'string'):
                        key = map(str, key)

                # 非in操作
                else:
                    # SQL类型隐式转换: 查询条件str/unicode，db类型int
                    if (type(val) in (str, unicode)) and (f_type == 'int'):
                        val = int(val)

                    # SQL类型隐式转换: 查询条件int，db类型string
                    if (type(val) == int) and (f_type == 'string'):
                        val = str(val)

                # 值为False
                if not pw_op(key, val):
                    return False

            return True

        if data is None:
            data = cls.cache.get(table_name)
        return list(filter(condition, data))

    @classmethod
    def order_by(cls, table_name, order_str):
        """
        排序

        :param str table_name: 表名
        :param str order_str: 排序字符串 倒序用`-`
        """
        # 解析排序规则 (field, clause)
        order_clauses = []
        for field in order_str.split(","):
            if field.startswith('-'):
                field = field[1:].strip()
                order_clauses.append((field, -1))
            else:
                order_clauses.append((field.strip(), 1))

        def order_key(obj):
            """排序规则"""
            result = []
            for field, clause in order_clauses:
                # 降序
                if clause == -1:
                    result.append(-getattr(obj, field))
                # 升序
                elif clause == 1:
                    result.append(getattr(obj, field))

            return result

        records = cls.cache.get(table_name)
        return sorted(records, key=order_key)
